// Configuration system: 


// All configuration options are given as either a name/value pair or
// a flag name and are taken from the command line, environment
// variables, and a configuration file, in that order (i.e. command
// line arguments override anything in the environment variables or
// config file).

// Command line arguments to the simulator are seperated from the
// program's arguments with a triple dash ('---').  Normal argument
// names are preceeded by '--' and are followed by the value. Flag
// arguments are preceeded by a single '-' and stand alone. for
// example:

// ocean -v --- --num_cpus 8 -disable_stats

// would run "ocean -v" on 8 cpus with statistics disabled.

// Environment variables must be the name of the flag preceeded by
// SIM_.  For example, to set the configuration file to be used for
// all runs, you can set the environment variable SIM_configfile to
// the filename of your configuration file.

// The configuration file has one option per line as '<option>
// <value>' or just '<flag>'.

// The flags 'h', '?', and 'help' are reserved and are used to display
// all the available configuration options.

// To use the system, first create a Configuration object by passing a
// pointer to argc and the values of argv and envp to the constructor.
// A pointer to argc is needed to adjust it if command line options
// are present.  The constructor reads all options from the command
// line and environment variables, looks for a "configfile" option and
// reads the file given, or "config.sim" by default.

// Three functions are then provided to retrieve options:
// getBooleanArgument, getIntegerArgument, and getStringArgument. Each
// return a value of the indicated type.  The arguments to these
// functions are pretty self explanatory, look at their declarations
// below.  getIntegerArgument takes optional arguments flags, min, and
// max; flags can be one of the CONFIG_FLAGS below. Note that in general,
// modules should only call get*Argument once when constructed and
// cache the values locally.

// get*Argument all take a description argument which is used when
// displaying the available configuration options. Note that when the
// options are being displayed, they still parsed and returned. So,
// for example, if the tcc flag is present, then a tcc cache will be
// instantiated and the tcc specific options will be displayed.

// When the simulator has been configured, checkUnused can be called
// to determine if all the options present have been used. printArgs
// prints the result of all get*Argument calls, likewise getArgs
// returns an array of arg structures for anybody thats want to print
// out/know all the options that are in use.


#ifndef __CONFIGURATION_H__
#define __CONFIGURATION_H__

#include "util/List.h"
#include "util/Logger.h"
#include "util/Pool.h"

typedef enum {
    CONFIG_POW2     = 1,
    CONFIG_UNSIGNED = 2,
    CONFIG_RANGE    = 4,
    CONFIG_MIN      = 8,
} CONFIG_FLAGS;

typedef enum {
    ARG_BOOLEAN,
    ARG_INTEGER,
    ARG_UNSIGNED,
    ARG_STRING,
} ARG_TYPE;

// an arg is an argument that has been gotten by the program, 
// wether it was in the user_args or not
struct arg {
    char* name;
    ARG_TYPE type;
    void* value;
};


class Configuration {
public:
    NEW_DELETE_OPERATORS_H(Configuration,NoSuper);

    Configuration (int* argc, const char** argv, const char** envp, char* config_file = NULL);
    ~Configuration ();

    bool getBooleanArgument (const char* name, const char* desc, bool default_boolean = false);
    int getIntegerArgument (const char* name, 
                            const char* desc, 
                            int default_int,
                            int flags=0, 
                            int lo=0, int hi=0);
    uint getUnsignedArgument (const char* name, 
                              const char* desc, 
                              uint default_int,
                              int flags=0, 
                              uint lo=0, uint hi=0);
    const char* getStringArgument (const char* name, const char* desc, const char* default_str);

    arg* getArgs();
    void checkUnused();
    void printArgs(Logger* log, SEVERITY sev);
    

  private:    
    enum {
        MAX_ARGS = 1000,
        STR_BUFFER_SIZE = 1024
    };

    enum {
        SRC_CMDLINE,
        SRC_ENV,
        SRC_FILE
    };

    // an user_arg is a user supplied argument
    struct user_arg {
        const char* name;
        const char* value;
        bool used;
        int source;
        int line;
    };

    bool show_usage;
    bool dump_config;

    int uarg_count;
    user_arg uargs[MAX_ARGS];

    int arg_count;
    arg args[MAX_ARGS];

    char* env_data;
    char* file_data;

    Logger CfgLog;

    void getArgument (const char* name, 
                      const char* desc, 
                      ARG_TYPE type, 
                      void* data,
                      int flags=0, 
                      int lo=0, int hi=0);

    bool uArgExists(const char* name);
    void addArg(const char* name, ARG_TYPE type, void* value);
    void addUArg(int source, const char* arg, const char* val, int line);
    void validateArg(user_arg* arg, ARG_TYPE type, int flags, int lo, int hi, void* data);
    void readEnvironment(const char** envp);
    void parseCommandLineArgs (int* argc, const char** argv);
    void readConfigFile(const char* configfile);

    const char* getSourceName(int source);
    int findUArg(const char* name);
    int findArg(const char* name);
};

extern Configuration* global_cfg;

#endif // __CONFIGURATION_H__
